package org.zoomdev.zoom.http;

import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.io.Io;
import org.zoomdev.zoom.common.json.JSON;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;

public class Request {


    public static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8");
    public static final MediaType FORM_TYPE = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");
    public static final MediaType TEXT_TYPE = MediaType.parse("text/html; charset=utf-8");
    final okhttp3.Request.Builder builder;
    okhttp3.MultipartBody.Builder multipartBuilder;
    FormBody.Builder formBodyBuilder;

    RequestBody requestBody;


    private final Method method;


    public Request(String url, Method method) {
        this.builder = new okhttp3.Request.Builder().url(url);
        this.method = method;
    }

    public static Request form(String url, Map<String, String> data) {
        Request request = new Request(url, Method.POST);
        request.form(data);
        return request;
    }

    public static Request post(String url) {
        Request request = new Request(url, Method.POST);
        return request;
    }

    public static Request upload(String url, Map<String, Object> data) {
        Request request = new Request(url, Method.POST);
        request.upload(data);
        return request;
    }

    public static Request get(String url) {
        return new Request(url, Method.GET);
    }

    public static Request json(String url, Object body) {
        Request request = new Request(url, Method.POST);
        request.json(body);
        return request;
    }


    public static Request jsonRaw(String url, String src) {
        return jsonRaw(url, src.getBytes(StandardCharsets.UTF_8));
    }

    public static Request jsonRaw(String url, byte[] content) {
        Request request = new Request(url, Method.POST);
        request.body(JSON_TYPE, content);
        return request;
    }

    public Request form(Map<String, String> data) {
        formBodyBuilder = new FormBody.Builder();
        for (Map.Entry<String, String> entry : data.entrySet()) {
            formBodyBuilder.add(entry.getKey(), entry.getValue());
        }
        return this;
    }


    /**
     * 创建一个json方式上传的request
     *
     * @param value
     * @return
     */
    public Request json(Object value) {
        assert (value != null);
        requestBody = FormBody.create(JSON_TYPE, JSON.stringify(value));
        return this;
    }

    public Request text(String value) {
        assert (value != null);
        requestBody = FormBody.create(TEXT_TYPE, value);
        return this;
    }

    public Request body(String contentType, byte[] content) {
        assert (contentType != null);
        assert (content != null);
        requestBody = RequestBody.create(MediaType.parse(contentType), content);
        return this;
    }

    Request body(MediaType mediaType, byte[] content) {
        assert (content != null);
        requestBody = RequestBody.create(mediaType, content);
        return this;
    }


    /**
     * 增加一个表单文件域
     *
     * @param key
     * @param file
     * @return
     * @throws IOException
     */
    public Request addFile(String key, File file) throws IOException {
        if (multipartBuilder == null) {
            multipartBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        }
        multipartBuilder.addFormDataPart(key, file.getName(), RequestBody.create(MultipartBody.FORM, Io.readBytes(file)));
        return this;
    }

    /**
     * 增加一个表单文件域
     *
     * @param key
     * @param fileName
     * @param bytes
     * @return
     * @throws IOException
     */
    public Request addBytes(String key, String fileName, byte[] bytes) throws IOException {
        if (multipartBuilder == null) {
            multipartBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        }
        multipartBuilder.addFormDataPart(key, fileName, RequestBody.create(MultipartBody.FORM, bytes));
        return this;
    }

    /**
     * 增加一个表单文件域
     *
     * @param key
     * @param bytes
     * @return
     * @throws IOException
     */
    public Request addBytes(String key, byte[] bytes) throws IOException {
        return addBytes(key, key, bytes);
    }

    /**
     * 增加一个表单文件域
     *
     * @param key
     * @param stream
     * @return
     * @throws IOException
     */
    public Request addStream(String key, InputStream stream) throws IOException {
        return addStream(key, key, stream);
    }

    /**
     * 增加一个表单文件域
     *
     * @param key
     * @param fileName
     * @param stream
     * @return
     * @throws IOException
     */
    public Request addStream(String key, String fileName, InputStream stream) throws IOException {
        if (multipartBuilder == null) {
            multipartBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        }
        try {
            multipartBuilder.addFormDataPart(key, fileName, RequestBody.create(MultipartBody.FORM, readBytes(stream)));
        } finally {
            Io.close(stream);
        }
        return this;
    }

    public static byte[] readBytes(InputStream stream) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Io.copyAndClose(stream, byteArrayOutputStream);
        return byteArrayOutputStream.toByteArray();

    }

    /**
     * 增加一个表单值， 注意，此方法一旦调用，一定生成一个表单数据，所以json方式不能使用这个方法
     *
     * @param key
     * @param value
     * @return
     */
    public Request addValue(String key, Object value) {
        if (multipartBuilder != null) {
            multipartBuilder.addFormDataPart(key, String.valueOf(value));
            return this;
        }

        if (formBodyBuilder == null) {
            formBodyBuilder = new FormBody.Builder();
        }
        formBodyBuilder.add(key, String.valueOf(value));
        return this;
    }

    public Request upload(Map<String, Object> data) {
        multipartBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        for (Map.Entry<String, Object> entry : data.entrySet()) {
            Object value = entry.getValue();
            if (value == null) {
                continue;
            }
            String key = entry.getKey();
            if (value instanceof File) {
                try {
                    multipartBuilder.addFormDataPart(key, key, RequestBody.create(MultipartBody.FORM, Io.readBytes((File) value)));
                } catch (IOException e) {
                    throw new ZoomException("上传文件读取文件失败,key:" + key, e);
                }
            } else if (value instanceof InputStream) {
                try {
                    multipartBuilder.addFormDataPart(key, key, RequestBody.create(MultipartBody.FORM, readBytes((InputStream) value)));
                } catch (IOException e) {
                    throw new ZoomException("上传文件读取stream失败,key:" + key, e);
                } finally {
                    Io.closeAny(value);
                }
            } else if (value.getClass() == byte[].class) {
                multipartBuilder.addFormDataPart(key, key, RequestBody.create(MultipartBody.FORM, (byte[]) value));
            } else {
                multipartBuilder.addFormDataPart(key, String.valueOf(value));
            }
        }
        return this;
    }

    public Request headers(Map<String, String> headers) {
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            builder.header(entry.getKey(), entry.getValue());
        }
        return this;
    }

    public Request header(String key, String value) {
        builder.header(key, value);
        return this;
    }

    okhttp3.Request build() {
        if (method == Method.POST) {
            if (requestBody != null) {
                builder.post(requestBody);
            } else if (multipartBuilder != null) {
                builder.post(multipartBuilder.build());
            } else if (formBodyBuilder != null) {
                builder.post(formBodyBuilder.build());
            } else {
                throw new ZoomException("POST方法必须设置post数据");
            }
        }


        return builder.build();
    }


    public enum Method {
        GET,
        POST
    }
}
